package com.example.springboottest.example.clickhouse;

import ru.yandex.clickhouse.BalancedClickhouseDataSource;
import ru.yandex.clickhouse.settings.ClickHouseProperties;

import java.sql.*;
import java.util.Arrays;
import java.util.List;

/**
 * ClickHouse的底层访问接口支持TCP和HTTP两种协议，其中，
 * TCP协议拥有更好的性能，其默认端口为9000，主要用于集群间的内
 * 部通信及CLI客户端；而HTTP协议则拥有更好的兼容性，可以通过
 * REST服务的形式被广泛用于JAVA、Python等编程语言的客户端，其默
 * 认端口为8123。通常而言，并不建议用户直接使用底层接口访问
 * ClickHouse，更为推荐的方式是通过CLI和JDBC这些封装接口，因为
 * 它们更加简单易用。
 */
public class ClickHouseDemo {
    public static void main(String[] args) {
//        create();
        delete();
        select();
    }

    /**
     * Clickhouse是个分析型数据库。这种场景下，数据一般是不变的，因此Clickhouse对update、delete的支持是比较弱的，
     * 实际上并不支持标准的update、delete操作。
     * 标准SQL的更新、删除操作是同步的，即客户端要等服务端反回执行结果（通常是int值）；
     * 而Clickhouse的update、delete是通过异步方式实现的，当执行update语句时，服务端立即反回，
     * 但是实际上此时数据还没变，而是排队等着。
     * <p>
     * ALTER TABLE [db.]table DELETE WHERE filter_expr
     *
     * 如果是uuid类型需要用函数，不能用等号
     * alter table DB_TEST.feature_family_combine
     * delete where notEquals('id','1')
     */
    public static void delete() {
        String sql = "ALTER TABLE DB_TEST.stu DELETE where id = ?";
        try {
            PreparedStatement ps = singleDatasource().prepareStatement(sql);
            ps.setInt(1, 13);
            int num = ps.executeUpdate();
            System.out.println("共执行 " + num + " 条语句.");
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    /**
     * Clickhouse是个分析型数据库。这种场景下，数据一般是不变的，因此Clickhouse对update、delete的支持是比较弱的，
     * 实际上并不支持标准的update、delete操作。
     * 标准SQL的更新、删除操作是同步的，即客户端要等服务端反回执行结果（通常是int值）；
     * 而Clickhouse的update、delete是通过异步方式实现的，当执行update语句时，服务端立即反回，
     * 但是实际上此时数据还没变，而是排队等着。
     * <p>
     * ALTER TABLE [db.]table UPDATE column1 = expr1 [, ...] WHERE filter_expr
     */
    public static void update() {
        String sql = "ALTER TABLE DB_TEST.stu UPDATE name=? where id = ?";
        try {
            PreparedStatement ps = singleDatasource().prepareStatement(sql);
            ps.setString(1, "jimmy");
            ps.setInt(2, 1);
            int num = ps.executeUpdate();
            System.out.println("共执行 " + num + " 条语句.");
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    /**
     * MergeTree的主键（PRIMARY KEY）只是用来生成一级索引（primary.idx）的，并没有唯一性约束这样的语义。
     * <p>
     * ReplacingMergeTree通过ORDER BY，表示判断唯一约束的条件。当分区合并之时，根据ORDER BY排序后，相邻重复的数据会被排除。
     */
    public static void create() {
        String sql = "INSERT INTO DB_TEST.stu(id, name, address) VALUES(?, ?, ?)";
        try {
            PreparedStatement ps = singleDatasource().prepareStatement(sql);
//            List<String> names = Arrays.asList("tom");
            List<String> names = Arrays.asList("tom", "jack", "frank", "rose", "kate", "fn", "liping", "neo", "masa"
                    , "张三", "李四", "王五", "赵六");

            //索引从1开始
            for (int i = 0; i < names.size(); i++) {
                ps.setInt(1, i + 1);
                ps.setString(2, names.get(i));
                if (i % 3 == 0)
                    ps.setString(3, "武汉");
                else if (i % 3 == 1)
                    ps.setString(3, "上海");
                else
                    ps.setString(3, "北京");

                ps.addBatch();
            }

            int[] num = ps.executeBatch();
            System.out.println("共执行 " + num.length + " 条语句.");
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public static void select() {
        String sql = "select * from DB_TEST.stu";
        try {
            ResultSet rs = singleDatasource().createStatement().executeQuery(sql);
            while (rs.next()) {
                // ResultSet 的下标值从 1 开始，不可使用 0，否则越界，报 ArrayIndexOutOfBoundsException 异常
                System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t"
                        + rs.getString(3));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public static Connection singleDatasource() {
        Connection con = null;
        try {
            // 初始化驱动
            Class.forName("ru.yandex.clickhouse.ClickHouseDriver");
            // url
            String url = "jdbc:clickhouse://localhost:8123/default";
            // 用户名密码
            String user = "default";
            String password = ""; // 登录
            con = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return con;
    }

    /**
     * 高可用模式允许设置多个host地址，每次会从可用的地址中随机
     * 选择一个进行连接
     */
    public static void multiDatasource() {
        try {
            // 初始化驱动
            Class.forName("ru.yandex.clickhouse.ClickHouseDriver");
            // url
            String url = "jdbc:clickhouse://localhost:8123/default,localhost:8123/default";
            // 用户名密码
            String user = "default";
            String password = ""; // 登录
            ClickHouseProperties clickHouseProperties = new ClickHouseProperties();
            clickHouseProperties.setUser("default"); //声明数据源
            BalancedClickhouseDataSource balanced = new BalancedClickhouseDataSource(url, clickHouseProperties); //对每个host进行ping操作, 排除不可用的dead连接
            balanced.actualize(); //获得JDBC连接
            Connection con = balanced.getConnection();
            Statement stmt = con.createStatement(); //查询
            ResultSet rs = stmt.executeQuery("SELECT 1 , hostName()");
            rs.next();
            System.out.println("res " + rs.getInt(1) + "," + rs.getString(2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
